package com.hibase.core.excel.context.method.support;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.hibase.common.exception.HibaseAssert;
import com.hibase.core.excel.EasyExcel;
import com.hibase.core.excel.annotation.HibaseRequestExcel;
import com.hibase.core.excel.data.HibaseAnalysisProcessor;
import com.hibase.core.excel.data.ReadAnalysisProcessor;
import com.hibase.core.excel.util.ExcelConfigUtil;
import com.hibase.core.excel.vo.dynamic.DynamicExcelResult;
import lombok.Setter;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.List;

/**
 * 解析HibaseRequestExcel参数
 *
 * @author <a href="mailto:xiongliyi@hichain.com">Ian Xiong</a>
 * @since 1.4.35-V2
 */
public class HandlerRequestExcelMethodArgumentResolver implements HandlerMethodArgumentResolver {
    private static final String TEMPLATE_CODE = "templateCode";
    private static final String IGNORE_EMPTY_ROW = "ignoreEmptyRow";
    private static final String HEAD_ROW_NUMBER = "headRowNumber";

    @Setter
    private ExcelConfigUtil excelConfigUtil;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(HibaseRequestExcel.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HibaseRequestExcel hibaseRequestExcel = obtainHibaseRequestExcel(parameter);
        HttpServletRequest httpServletRequest = obtainHttpServletRequest(webRequest);

        // init params
        Integer headRowNumber = hibaseRequestExcel.headRowNumber();
        Boolean ignoreEmptyRow = hibaseRequestExcel.ignoreEmptyRow();
        Class<? extends ReadAnalysisProcessor<?>> readAnalysisProcessor = hibaseRequestExcel.readAnalysisProcessor();


        // get config id
        String templateCode = obtainHttpRequestValue(httpServletRequest, TEMPLATE_CODE);
        String reqIgnoreEmptyRow = obtainHttpRequestValue(httpServletRequest, IGNORE_EMPTY_ROW);
        String reqHeadRowNumber = obtainHttpRequestValue(httpServletRequest, HEAD_ROW_NUMBER);

        if (StrUtil.isNotEmpty(reqIgnoreEmptyRow)) {
            ignoreEmptyRow = Convert.toBool(reqIgnoreEmptyRow);
        }
        if (StrUtil.isNotEmpty(reqHeadRowNumber)) {
            headRowNumber = Convert.toInt(reqHeadRowNumber);
        }


        // read Excel auto close inputStream TODO:processor multipart sheet
        ReadAnalysisProcessor<?> analysisProcessor = null;
        Class<?> targetClass = null;

        if (hibaseRequestExcel.requireTemplateCode()) {

            targetClass = excelConfigUtil.createTemplate(templateCode);
            headRowNumber = excelConfigUtil.getExcelTemplateInfo().getHeadRowNum();
            ignoreEmptyRow = excelConfigUtil.getExcelTemplateInfo().getBeIgnoreEmptyRow();

            // custom read listener
            if (excelConfigUtil.getReadAnalysisProcessor() != null) {
                readAnalysisProcessor = excelConfigUtil.getReadAnalysisProcessor();
                analysisProcessor = BeanUtils.instantiateClass(readAnalysisProcessor);
            } else {
                analysisProcessor = BeanUtils.instantiateClass(readAnalysisProcessor);
                ((HibaseAnalysisProcessor) analysisProcessor).setDefaultValueMap(excelConfigUtil.getDefaultValueMap());
            }

        } else {
            targetClass = ResolvableType.forMethodParameter(parameter).getGeneric(0).resolve();
            analysisProcessor = BeanUtils.instantiateClass(readAnalysisProcessor);
        }


        InputStream inputStream = null;
        if (httpServletRequest instanceof MultipartRequest) {
            MultipartRequest multipartRequest = (MultipartRequest) httpServletRequest;
            MultipartFile file = multipartRequest.getFile(hibaseRequestExcel.fileName());

            HibaseAssert.notNull(file, "Uploaded files cannot be empty");

            inputStream = file.getInputStream();

        } else {
            inputStream = httpServletRequest.getInputStream();
        }


        EasyExcel.read(inputStream, targetClass, analysisProcessor)
                .headRowNumber(headRowNumber)
                .ignoreEmptyRow(ignoreEmptyRow)
                .sheet(0)
                .doRead();

        WebDataBinder binder = binderFactory.createBinder(webRequest, analysisProcessor.getErrors(), "Excel");
        ModelMap model = mavContainer.getModel();
        model.put(BindingResult.MODEL_KEY_PREFIX.concat("excel"), binder.getBindingResult());

        if (analysisProcessor instanceof HibaseAnalysisProcessor) {
            HibaseAnalysisProcessor hibaseAnalysisProcessor = (HibaseAnalysisProcessor) analysisProcessor;
            DynamicExcelResult dynamicExcelResult = hibaseAnalysisProcessor.getDynamicResult();
            // 设置行号、忽略空行
            excelConfigUtil.getExcelTemplateInfo().setHeadRowNum(headRowNumber);
            excelConfigUtil.getExcelTemplateInfo().setBeIgnoreEmptyRow(ignoreEmptyRow);

            dynamicExcelResult.setConfigInfo(excelConfigUtil.getExcelTemplateInfo());
            dynamicExcelResult.setBindingResult(binder.getBindingResult());

            return dynamicExcelResult;
        }


        return analysisProcessor.getData();
    }


    private HibaseRequestExcel obtainHibaseRequestExcel(MethodParameter parameter) {
        Class<?> parameterType = parameter.getParameterType();
        if (!parameterType.isAssignableFrom(List.class)) {
            if (!parameterType.isAssignableFrom(DynamicExcelResult.class)) {
                throw new IllegalArgumentException("Parameter 'noList' not found. Available parameters are [collection, list]");
            }
        }

        HibaseRequestExcel hibaseRequestExcel = parameter.getParameterAnnotation(HibaseRequestExcel.class);
        HibaseAssert.notNull(hibaseRequestExcel, "HibaseRequestExcel annotation cannot be null");

        return hibaseRequestExcel;
    }

    private HttpServletRequest obtainHttpServletRequest(NativeWebRequest webRequest) {
        HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        HibaseAssert.notNull(httpServletRequest, "HttpServletRequest cannot be null");
        return httpServletRequest;
    }


    private String obtainHttpRequestValue(HttpServletRequest request, String name) {
        return request.getParameter(name);
    }


}
